package sky.cin.framework.nio;

import sky.cin.framework.log.Croog;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import static sky.cin.framework.nio.NioUtils.read;
import static sky.cin.framework.nio.NioUtils.write;

public class NioEventLoop {

    private String name;
    private SyncableSelector selector;
    private ServerSocketChannel channel;
    private List<NioEventLoop> subReceivers;
    private int capacity;
    private int size;
    private Croog log;

    private NioEventLoop(int capacity, int ops, SocketChannel client, NioHandler handler, int index) {
        this.name = String.format("连接处理器<%d>", index);
        this.log = new Croog("sky.cin.framework.nio.NioEventLoop");
        this.capacity = capacity;
        this.selector = SyncableSelector.open();
        log.info(String.format("创建了一个新的%s", this.name));
        register(client, ops);
        listen(handler);
    }

    public NioEventLoop(int port, NioHandler handler, SyncableSelector selector) {
        try {
            this.name = "服务端连接监听器";
            this.log = new Croog("sky.cin.framework.nio.NioEventLoop");
            this.selector = selector == null ? SyncableSelector.open() : selector;
            this.channel = ServerSocketChannel.open();
            this.channel.socket().bind(new InetSocketAddress(port));
            this.channel.configureBlocking(false);
            this.channel.register(this.selector.instance(), SelectionKey.OP_ACCEPT);

            this.subReceivers = new ArrayList<>();

            log.info(String.format("%s启动完毕", this.name));

            this.listen((key, msg) -> {
                int option = SelectionKey.OP_READ;
                SocketChannel client = this.channel.accept();
                for (NioEventLoop subReceiver : subReceivers) {
                    if (!subReceiver.isFull()) {
                        subReceiver.register(client, option);
                        return null;
                    }
                }
                put(option, client, handler);
                return null;
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public NioEventLoop(int port, NioHandler handler) {
        this(port, handler, null);
    }

    private void put(int option, SocketChannel client, NioHandler handler) {
        NioEventLoop event = new NioEventLoop(10, option, client, handler, subReceivers.size());
        subReceivers.add(event);
    }

    public void listen(NioHandler handler) {
        new Thread(() -> {
            SocketChannel channel = null;
            SelectionKey key = null;
            while (true) {
                try {
                    selector.select();
                    Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                    while (iterator.hasNext()) {
                        key = iterator.next();
                        if (key.isAcceptable()) {
                            handler.handle(key, "");
                        }
                        if (key.isReadable()) {
                            channel = (SocketChannel) key.channel();
                            String request = read(channel);
                            if ("".equals(request)) {
                                doClose(key, channel);
                            } else {
                                String response = handler.handle(key, request);
                                if (response != null) {
                                    write(channel, response);
                                }
                            }
                        }
                        iterator.remove();
                    }
                } catch (IOException e) {
                    try {
                        doClose(key, channel);
                    } catch (IOException ioException) {
                        ioException.printStackTrace();
                    }
                }
            }
        }).start();
    }

    public void doClose(SelectionKey key, SocketChannel channel) throws IOException {
        size--;
        if (key != null) {
            key.cancel();
        }
        if (channel != null) {
            log.error(String.format("%s 从%s中注销, 当前处理器已使用%d0%%", channel.getRemoteAddress(), this.name, size));
            channel.close();
        }
    }

    public void register(SelectableChannel channel, int ops) {
        try {
            selector.register(channel, ops);
            size++;
            log.info(String.format("%s 注册到了%s, 当前处理器已使用%d0%%", ((SocketChannel) channel).getRemoteAddress(), this.name, size));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public boolean isFull() {
        return size >= capacity;
    }

    public boolean isActive(SocketChannel channel) {
        return channel.isOpen() && channel.isConnected();
    }

}